package com.sinosoft.taxbenefit.entry.common.base;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.sinosoft.platform.base.TaxBaseService;
import com.sinosoft.platform.common.exception.BusinessDataErrException;
import com.sinosoft.platform.common.exception.RequestDateException;
import com.sinosoft.platform.common.exception.SignErrException;
import com.sinosoft.platform.common.util.DateUtil;
import com.sinosoft.taxbenefit.api.config.ENUM_DISPOSE_RESULT;
import com.sinosoft.taxbenefit.api.config.ENUM_DISPOSE_TYPE;
import com.sinosoft.taxbenefit.api.dto.common.FaultTaskDTO;
import com.sinosoft.taxbenefit.api.dto.request.TaxFaulTaskDTO;
import com.sinosoft.taxbenefit.api.dto.request.base.BaseRequestTax;
import com.sinosoft.taxbenefit.api.dto.request.base.CoreRequestHead;
import com.sinosoft.taxbenefit.api.dto.request.base.CoreResponseHead;
import com.sinosoft.taxbenefit.api.dto.request.base.CoreResponseTax;
import com.sinosoft.taxbenefit.api.dto.request.base.ParamHeadDTO;
import com.sinosoft.taxbenefit.api.dto.request.base.ThirdSendResponseDTO;
import com.sinosoft.taxbenefit.entry.business.config.ENUM_REPORTLOG_DEALTYPE;
import com.sinosoft.taxbenefit.entry.business.config.ENUM_STATEMENT;
import com.sinosoft.taxbenefit.entry.business.config.TaxWebConstants;
import com.sinosoft.taxbenefit.entry.business.dto.ErrorViewLogDTO;
import com.sinosoft.taxbenefit.entry.business.dto.ReceiveLogDTO;
import com.sinosoft.taxbenefit.entry.business.dto.RequestServicePortDTO;
import com.sinosoft.taxbenefit.entry.business.service.CommonService;
import com.sinosoft.taxbenefit.entry.business.service.LogService;
import com.sinosoft.taxbenefit.entry.common.service.CommonCodeService;
import com.sinosoft.taxbenefit.entry.common.service.TaxFaultTaskService;
import com.sinosoft.taxbenefit.entry.common.utils.ReportSecurityUtil;

@Service
public abstract class TaxBaseRequestWebService extends TaxBaseService {

	@Autowired
	private CommonService commonService;
	@Autowired
	private LogService reportLogService;
	@Autowired
	private TaxFaultTaskService faultTaskService;
	@Autowired
	private CommonCodeService commonCodeService;
	
	/**
	 * 接口模板
	 * @param requestJson
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public String dealMainBiz(String requestJson) {
		logger.info("请求报文：" + requestJson);
		// 1.准备需要对象和字段
		ReceiveLogDTO receiveDTO = new ReceiveLogDTO();// 请求报文DTO
		ErrorViewLogDTO errorViewLogDTO = null;// 系统接口异常记录DTO
		String responseWhole = null;	// 返回核心报文内容
		CoreRequestHead header = new CoreRequestHead();// 请求报文头
		RequestServicePortDTO portDTO = new RequestServicePortDTO();//
		ThirdSendResponseDTO responseDTO = new ThirdSendResponseDTO();
		try {
			// 2.解析报文
			Class parentClass = getJsonParentClass();
			BaseRequestTax jsonTax;
			try {
				jsonTax = JSONObject.parseObject(requestJson, parentClass);
			} catch (Exception e) {
				throw new RequestDateException("报文数据格式异常");
			}
			if(jsonTax.getBody()==null){
				throw new NullPointerException();
			}
			header = jsonTax.getRequestHead();
			receiveDTO.setSerialNo(header.getSerialNo());
			receiveDTO.setSourceCode(header.getSourceCode());
			receiveDTO.setBusinessType(header.getTransType());
			receiveDTO.setRequestJson(requestJson);
			receiveDTO.setRequestIp("");
			// 3.获取当前请求服务编码信息DTO
			portDTO = this.getRequestServicePortDTO(header.getTransType());
			// 封装报文DTO
			receiveDTO.setIsSync(portDTO.getAsynFlag());
			// 判断接口是否启用
			if (TaxWebConstants.NO.equals(portDTO.getPortFlag())) {
				throw new BusinessDataErrException(TaxWebConstants.PORTUNDEFINED);
			}
			// 校验报文合法性(暂不验签)
//			this.checkRequestValid(JSONObject.toJSONString(jsonTax.getBody()), header.getSignKey());
			// 重复请求校验
			this.commonService.checkAlikeRequest(header.getSerialNo());
			 //判断同步异步处理
			if (ENUM_REPORTLOG_DEALTYPE.SYNC_DEAL.code().equals(portDTO.getAsynFlag())) {
				// 向中保信发送报文
				ParamHeadDTO paramHeadDTO=new ParamHeadDTO();
				paramHeadDTO.setAreaCode(header.getAreaCode());
				paramHeadDTO.setRecordNum(header.getRecordNum());
				paramHeadDTO.setSerialNo(header.getSerialNo());
				paramHeadDTO.setPortCode(portDTO.getPortCode());
				paramHeadDTO.setPortName(portDTO.getPortName());
				// 响应核心的报文体对象
				responseDTO = this.callBizService(jsonTax,paramHeadDTO);
				receiveDTO.setState(ENUM_STATEMENT.REQUEST_SUCCESS.code());// 状态
				receiveDTO.setDescription(ENUM_STATEMENT.REQUEST_SUCCESS.desc());
			} else {
				// 异步处理
				this.addFaultTask(header.getSerialNo(), portDTO.getPortCode(), portDTO.getPortName());
				receiveDTO.setState(ENUM_STATEMENT.DEAL_ING.code());
				receiveDTO.setDescription(ENUM_STATEMENT.DEAL_ING.desc());
				responseDTO.setResultCode(ENUM_STATEMENT.DEAL_ING.code());
				responseDTO.setResultDesc(ENUM_STATEMENT.DEAL_ING.desc());
				responseDTO.setResJson("异步" + ENUM_STATEMENT.DEAL_ING.desc());
			}
		}catch(NullPointerException ex){
			logger.error("报文格式错误");
			responseDTO.setResultCode(ENUM_STATEMENT.VERIFY_FAILED.code());
			responseDTO.setResultDesc(ENUM_STATEMENT.VERIFY_FAILED.desc());
			responseDTO.setResJson(ENUM_STATEMENT.MESSAGE_ERROR.desc());
			receiveDTO.setState(ENUM_STATEMENT.MESSAGE_ERROR.code());
			receiveDTO.setDescription(ENUM_STATEMENT.MESSAGE_ERROR.desc());
		}catch(SignErrException ex){
			logger.error(ex.getMessage());
			//验签错误
			responseDTO.setResultCode(ENUM_STATEMENT.VERIFY_FAILED.code());
			responseDTO.setResultDesc(ENUM_STATEMENT.VERIFY_FAILED.desc());
			responseDTO.setResJson(ex.getMessage());
			receiveDTO.setState(ENUM_STATEMENT.VERIFY_FAILED.code());
			receiveDTO.setDescription(ex.getMessage());
		}catch(RequestDateException e){
			logger.error(e.getMessage());
			responseDTO.setResultCode(ENUM_STATEMENT.VERIFY_FAILED.code());
			responseDTO.setResultDesc(ENUM_STATEMENT.VERIFY_FAILED.desc());
			responseDTO.setResJson(ENUM_STATEMENT.MESSAGE_ERROR.desc());
			receiveDTO.setState(ENUM_STATEMENT.MESSAGE_ERROR.code());
			receiveDTO.setDescription(ENUM_STATEMENT.MESSAGE_ERROR.desc());
		}
		catch(BusinessDataErrException ex){
			logger.error(ex.getMessage());
			responseDTO.setResultCode(ENUM_STATEMENT.VERIFY_FAILED.code());
			responseDTO.setResultDesc(ENUM_STATEMENT.VERIFY_FAILED.desc());
			responseDTO.setResJson(ex.getMessage());
			//接口可用
			if(!ex.getMessage().equals(TaxWebConstants.PORTUNDEFINED)){
				FaultTaskDTO taxFaultTaskDTO=faultTaskService.queryTaxFaultTaskBySerialNo(header.getSerialNo());
				//请求重复操作	
				if(taxFaultTaskDTO!=null){
					responseDTO = JSONObject.parseObject(taxFaultTaskDTO.getMessage(), ThirdSendResponseDTO.class);
				}else{
					responseDTO.setResJson(ex.getMessage());
					receiveDTO.setState(ENUM_STATEMENT.VERIFY_FAILED.code());
					receiveDTO.setDescription(ENUM_STATEMENT.VERIFY_FAILED.desc());
				}
			}else{
				responseDTO.setResJson(TaxWebConstants.PORTUNDEFINED);
				receiveDTO.setState(ENUM_STATEMENT.VERIFY_FAILED.code());
				receiveDTO.setDescription(ENUM_STATEMENT.VERIFY_FAILED.desc());				
			}	
		}catch (Exception ex) {
			errorViewLogDTO = this.initErrorViewLogDTO(portDTO.getPortCode(), header.getSerialNo());
			responseDTO.setResultCode(ENUM_STATEMENT.VERIFY_FAILED.code());
			responseDTO.setResultDesc(ENUM_STATEMENT.VERIFY_FAILED.desc());
			responseDTO.setResJson(errorViewLogDTO);
			receiveDTO.setState(ENUM_STATEMENT.VERIFY_FAILED.code());
			receiveDTO.setDescription(ENUM_STATEMENT.VERIFY_FAILED.desc());
			this.reportLogService.logError(errorViewLogDTO);
			logger.error(ex.getMessage());
		} finally {
			// 封装核心的响应报文
			responseWhole = JSONObject.toJSONString(this.createResponseTax(header, responseDTO));
			receiveDTO.setResponseJson(JSONObject.toJSONString(responseWhole));
			logger.info("返回报文为：" + responseWhole);
			// 记录请求报文
			reportLogService.logReport(receiveDTO);
		}
		return responseWhole;
	}
	
	/**
	 * 指定报文解析的json对象父类class 
	 * 直接return 根类.class即可
	 * 要求:返回的Class必须是CoreRequestTax.class 的子类
	 * @return 
	 */
	@SuppressWarnings("rawtypes")
	protected abstract Class getJsonParentClass();

	/**
	 * 封装响应核心返报文
	 * @param requestHead
	 *            核心请求的报文头
	 * @param businessType
	 *            请求类型
	 * @param obj
	 *            报文体
	 * @return 响应核心返报文
	 */
	private CoreResponseTax createResponseTax(CoreRequestHead requestHead, Object obj) {
		CoreResponseTax res = new CoreResponseTax();
		CoreResponseHead head = new CoreResponseHead();
		head.setSerialNo(requestHead.getSerialNo());
		head.setTransDate(requestHead.getTransDate());
		// 加密签名
		String sign = ReportSecurityUtil.encryptHXReportByBody(JSONObject.toJSONString(obj));
		head.setSignKey(sign);
		res.setBody(obj);
		res.setHead(head);
		return res;
	}

	/**
	 * 方法功能描述 
	 * 1,本系統报文体对象转换中保信封装DTO对象,
	 * 2,向中报信发送请求报文 
	 * 3,记录发送中保信的报文信息
	 * 4,返回核心要的报文对象
	 * 
	 * @param ParamHeadDTO DTO包括交易流水号,地区代码,请求次数
	 *          
	 * @return 返回响应报文
	 */
	protected abstract ThirdSendResponseDTO callBizService(Object obj,ParamHeadDTO paramHeadDTO);

	/**
	 * 获取请求服务编码信息DTO
	 * 
	 * @return 接口DTO
	 */
	protected  RequestServicePortDTO getRequestServicePortDTO(String portCode){
		return commonCodeService.queryTaxServerPortDTOByPortCode(portCode);
	}
	
	/**
	 * 检查核心报文是否合法
	 * @Title: checkRequestValid
	 * @param requestJson
	 * @param sign 
	 */
	private void checkRequestValid(String requestJson, String sign) {
		Boolean flag =  ReportSecurityUtil.checkHXReport(requestJson, sign);
		if(!flag){
			throw new SignErrException("签名验证不合法,不合法签名为:" + sign);
		}
	}

	/**
	 * 封装异常报文DTO
	 * @param portCode 请求服务接口
	 * @param serialNo 流水号
	 * @param errorInfo 错误信息
	 * @return
	 */
	private ErrorViewLogDTO initErrorViewLogDTO(String portCode,String serialNo) {
		ErrorViewLogDTO errorViewLogDTO = new ErrorViewLogDTO();	
		errorViewLogDTO.setSerialNo(serialNo);
		errorViewLogDTO.setInterCode(portCode);
		errorViewLogDTO.setErroType(ENUM_STATEMENT.DEAL_ING.code());
		errorViewLogDTO.setErrorInfo(ENUM_STATEMENT.DEAL_ING.desc());
		return errorViewLogDTO;
	}

	/**
	 * 添加异常任务表记录
	 * 
	 * @param serialNo 交易流水号
	 * @param portCode 请求接口编码
	 * @param portName 接口名称     
	 */
	private void addFaultTask(String serialNo, String portCode,String portName) {
		TaxFaulTaskDTO taxFaultTaskDTO = new TaxFaulTaskDTO();
		taxFaultTaskDTO.setSerialNo(serialNo);
		taxFaultTaskDTO.setServerPortCode(portCode);
		taxFaultTaskDTO.setServerPortName(portName);
		taxFaultTaskDTO.setDisposeType(ENUM_DISPOSE_TYPE.ASYNC.code());
		faultTaskService.addTaxFaultTask(taxFaultTaskDTO);
	}

}
